#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
#define SIZE 1024
#define TYPE "[%s@%s %s]# "
#define MAXARGC 128
char* my_argv[MAXARGC];
int my_argc = 0;
char* my_env[MAXARGC];
int my_envs = 0;
int lastcode = 0;
const char* GetUserName() {
    const char* UserName = getenv("USER");
    return UserName == NULL ? "none" : UserName;
}
const char* GetHostName() {
    const char* HostName = getenv("HOSTNAME");
    return HostName == NULL ? "none" : HostName;
}
const char* GetPwd() {
    const char* Pwd = getenv("PWD");
    return Pwd == NULL ? "none" : Pwd;
}
void MakeCommendLine(char commend_temp[], int size) {
    snprintf(commend_temp, size, TYPE, GetUserName(), GetHostName(), GetPwd());
}
void PrintCommendPrompt() {
    char Commend[1024];
    MakeCommendLine(Commend, sizeof(Commend));
    printf("%s", Commend);
    fflush(stdout);
}
bool GetCommendLine(char* line, int size) {
    char* temp = fgets(line, size, stdin);
    if(temp == NULL) {
        return false;
    }
    line[strlen(temp) - 1] = '\0'; // 清空我们的'\n'
    if(strlen(temp) == 0) return false;
    return true;
}
void InitEnv() { 
    extern char** environ;
    memset(my_env, 0, sizeof(my_env));
    my_envs = 0;

    for(int i = 0; environ[i]; i++) {
        my_env[i] = (char*)malloc(strlen(environ[i]) + 1);
        strcpy(my_env[i], environ[i]);
        my_envs++;
    }
    my_env[my_envs++] = (char*)"MYENV=test";
    my_env[my_envs] = NULL;

    for(int i = 0; my_env[i]; i++) {
        putenv(my_env[i]);
    }
    environ = my_env;
}
void CommendAnalyse(char* commendline) {
    #define seq " "
    my_argv[my_argc++] = strtok(commendline, seq);
    while((my_argv[my_argc++] = strtok(NULL, seq)) != NULL) 
    my_argc--;
}
void Cd() {
    if(my_argc == 1) {
        string home = GetHostName();
        chdir(home.c_str());
    }else {
        string where = my_argv[1];
        if(where == "-") {
            // to do
        }else if(where == "~") {
            // to do
        }else {
            chdir(where.c_str());
        }
    }
}
void Echo() {
    if(my_argc == 2) {
        string opt = my_argv[1];
        if(opt == "$?") {
            printf("%d\n", lastcode);
            lastcode = 0;
        }else if(opt[0] == '$') {
            string str = opt.substr(1);
            const char* env_value = getenv(str.c_str());
            if(env_value) {
                printf("%s\n", env_value);
            }
        }else {
            printf("%s\n", opt.c_str());
        }
    }
}
bool CheckAndExecBuiltin() {
    string cmd = my_argv[0];
    if(cmd == "cd") {
        Cd();
        return true;
    }else if(cmd == "echo") {
        Echo();
        return true;
    }else if(cmd == "export") {
        // to do
    }else if(cmd == "alias") {
        // to do
    }
    return false;
}

void Execute() {
    pid_t id = fork();
    if(id == 0) // 子进程
    {
        execvp(my_argv[0], my_argv);
        exit(1);
    }
    // 父进程
    int status = 0;
    pid_t temp = waitpid(id, nullptr, 0);
    if(temp > 0) {
        lastcode = WEXITSTATUS(status);
    }  
}
void PrintArgv() {
    for(int i = 0; my_argv[i]; i++) {
        printf("argv[%d]->%s\n", i, my_argv[i]);
    }
    printf("argc: %d\n", my_argc);
}
void PrintEnv() {
    for(int i = 0; my_env[i]; i++) {
        printf("%s\n", my_env[i]);
    }
}
int main() {
    // shell启动的时候，我要从系统中获取环境变量，我们的环境变量应该是从我们的父进程shell来的。
    InitEnv();
    while(1) {
        // 输出命令行提示符
        PrintCommendPrompt();

        // 获取输入命令
        char commendline[SIZE];
        if(!GetCommendLine(commendline, sizeof(commendline))) {
            continue;
        }

        // 命令行分析
        CommendAnalyse(commendline);

        // 检测并处理内键命令
        if(CheckAndExecBuiltin()) {
            continue;
        } 
        PrintEnv();
        // PrintArgv();

        // 执行命令
        Execute();
    }
    return 0;
}